package org.tokaf;

import java.net.URI;
import java.util.ArrayList;

//import org.openrdf.model.impl.URIImpl;
import org.tokaf.datasearcher.DataModifier;
import org.tokaf.datasearcher.DataSearcher;
import org.tokaf.rater.InductionRater;

/**
 * <p> InducedRatings computes induced ratings, using methods from class
 * UserRatings. It is supposed to be once initialized with correct predicates
 * and types and when one individual of type changes, it computes computed
 * rating for it. Other situation is when rating of some individual <i>b</i>
 * connected to some individual <i>a</i> of current type changes, we must
 * recompute also the computed rating of <i>a</i>. </p> <p> Copyright (c) 2006
 * </p> <p>
 * @author Alan Eckhardt
 * @version 1.0
 */
public class InducedRatings {

	String attributeClassName;

	String connectingPredicates[];

	String objectsClassNames[];

	//String userRating;

	String namespace;

	QueryFinder queryFinder;

	DataSearcher master;

	DataModifier masterModifier;

	double weightsOnPredicates[];

	public InducedRatings(String namespace, QueryFinder queryFinder,
			DataSearcher master, DataModifier masterModifier,
			String attributeClassName, String connectingPredicates[],
			String objectsClassNames[]) {
		this.attributeClassName = attributeClassName;
		this.connectingPredicates = connectingPredicates;
		this.objectsClassNames = objectsClassNames;
		this.namespace = namespace;
		this.queryFinder = queryFinder;
		this.master = master;
		this.masterModifier = masterModifier;
	}

	protected String checkURI(String string) {
		URI a = java.net.URI.create(string);
		if (a.getAuthority() == null)
			return namespace + string;
		else
			return string;
	}

	/**
	 * Return ratings of entities who are connected to entityName by
	 * connectingPredicates[index].
	 * @param entityName is the name of entity of which we want to compute new
	 *            rating.
	 * @param index is index of predicate in field connectingPredicates.
	 * @return DataSearcher with ratings on entities connected by
	 *         connectingPredicates[index] with entityName
	 */
	protected DataSearcher getRatingOnConnected(String entityName, String userRating, int index) {
		ArrayList<String> params = new ArrayList<String>();
		params.add(checkURI(entityName));
		params.add(checkURI(connectingPredicates[index]));
		params.add(checkURI(userRating));
		String ratingQuery = queryFinder.getQuery("GetListOfSubjectsObjects",
				1, params);
		DataSearcher ratingSearcher = (DataSearcher) master.clone();
		ratingSearcher.initSearch(ratingQuery);
		return ratingSearcher;
	}

	/**
	 * Sets induced rating for user on all individuals of class, that was specified in constructor.
	 * @param user
	 */
	public void setIducedRatingOnClass(String user) {
		ArrayList<String> params = new ArrayList<String>();
		params.add(checkURI(attributeClassName));
		String ratingQuery = queryFinder.getQuery("GetListOfTypedEntities", 1,
				params);
		DataSearcher data = (DataSearcher) master.clone();
		data.initSearch(ratingQuery);
		while (data.hasNext()) {
			String name = data.getField(0).toString();
			this.setIducedRatingOnEntity(name, user);
			data.advance();
		}
	}

	/**
	 * <p> getPreferencesOnClass </p> Returns user preferences on specified
	 * class. The returned array is sorted to match the order of parameter
	 * ratings.
	 * @param ratings String[]
	 * @param type String
	 * @return double[]
	 */
	protected double[] getPreferencesOnClass(String className, String[] ratings) {
		double weights[] = new double[ratings.length];
		UserRatings ur = new UserRatings(namespace, queryFinder, master,
				masterModifier);
		for (int i = 0; i < ratings.length; i++) {
			ur.setRatingNames(ratings[i], null);
			try {
				weights[i] = ur.getUserRatingOnEntity(className);
			} catch (Exception ex) {
				weights[i] = 0;
			}
		}
		return weights;
	}

	/**
	 * <p> getWeightsOnTypes </p> Computes the weights associated by userRating
	 * to each of types.
	 * @param predicates String[]
	 * @param userRating String
	 * @return double[]
	 */
	protected double[] getWeightsOnTypes(String[] types, String userRating,
			String computedUserRating) {
		double[] weightsOnPredicates = new double[types.length];
		for (int j = 0; j < types.length; j++) {
			double[] temp2 = getPreferencesOnClass(types[j],
					new String[] { userRating });
			double[] temp = getPreferencesOnClass(types[j],
					new String[] { computedUserRating });
			if (temp != null && temp.length > 0)
				weightsOnPredicates[j] = temp[0];
			else if (temp2 != null && temp2.length > 0
					&& temp2[0] > weightsOnPredicates[j])
				weightsOnPredicates[j] = temp2[0];
			else
				weightsOnPredicates[j] = 0;
			if (temp2 != null && temp2.length > 0
					&& temp2[0] > weightsOnPredicates[j])
				weightsOnPredicates[j] = temp2[0];
		}
		return weightsOnPredicates;
	}

	/**
	 * Computes induced rating for entityName, which si of class
	 * attributeClassName.
	 * @param entityName is the name of entity
	 * @param user is the name of user
	 * @return the induced rating of entityName
	 */
	public double getInducedRatingOnEntity(String entityName, String user) {
		UserRatings ur = new UserRatings(namespace, queryFinder, master,
				masterModifier);
		ur.setUserName(user);
		weightsOnPredicates = getWeightsOnTypes(connectingPredicates, ur
				.getRatingName(), ur.getComputedRatingName());
		InductionRater ir = new InductionRater();
		ir.init(connectingPredicates, weightsOnPredicates);
		return ur.getInducedRatingOnEntity(attributeClassName, entityName,
				connectingPredicates, ir);
	}

	/**
	 * Computes and sets induced rating for entityName, which si of class
	 * attributeClassName.
	 * @param entityName is the name of entity
	 * @param user is the name of user
	 */
	public void setIducedRatingOnEntity(String entityName, String user) {
		UserRatings ur = new UserRatings(namespace, queryFinder, master,
				masterModifier);
		ur.setUserName(user);
		double rating = getInducedRatingOnEntity(entityName, user);
		ur.addUserRatingsOnEntity(entityName, new Double(rating), true, true);
	}

	/**
	 * 
	 * @param className
	 * @return index of specified class in objectsClassNames
	 */
	private int findIndexOfClass(String className) {

		className = checkURI(className);
		int i = 0;
		for (; i < this.objectsClassNames.length; i++) {
			if (checkURI(objectsClassNames[i]).equals(className))
				break;

		}
		return i;
	}

	/**
	 * 
	 * @param predicateName
	 * @return index of specified predicate in connectingPredicates
	 */
	private int findIndexOfPredicate(String predicateName) {
		predicateName = checkURI(predicateName);
		int i = 0;
		for (; i < this.connectingPredicates.length; i++) {
			if (checkURI(connectingPredicates[i]).equals(predicateName))
				break;
		}
		return i;
	}

	/**
	 * This function takes name and class of some object, that may be connected
	 * to the attribute class. It first find possible entities connected with
	 * entityName and for them computes induced rating.
	 * @param entityName
	 * @param className
	 * @param user
	 */
	public void setIducedRatingOnEntity(String entityName, String className,
			String user) {
		if (entityName == null || entityName.equals(""))
			return;
		if (className == null || className.equals(""))
			return;
		int index = findIndexOfClass(className);
		if (index == objectsClassNames.length)
			return;

		// We initialize new instance of UserRatings
		UserRatings ur = new UserRatings(namespace, queryFinder, master,
				masterModifier);
		ur.setUserName(user);
		weightsOnPredicates = getWeightsOnTypes(connectingPredicates,
				ur.getRatingName(), ur.getComputedRatingName());
		InductionRater ir = new InductionRater();
		ir.init(connectingPredicates, weightsOnPredicates);

		// First argument is connecting predicate
		ArrayList<String> params = new ArrayList<String>();
		params.add(checkURI(connectingPredicates[index]));

		// Second argument is the name of entity
		params.add(checkURI(entityName));

		String objectsQuery = queryFinder.getQuery("GetListOfEntities", 4,
				params);
		// data contains the objects connected to entityName by connecting
		// predicate
		DataSearcher searcher = (DataSearcher) master.clone();
		searcher.initSearch(objectsQuery);
		while (searcher.hasNext()) {
			String name = searcher.getField(0).toString();
			// Now we compute induced rating of attribute name.
			ur.setInducedRatings(attributeClassName, name,
					connectingPredicates, ir);
			searcher.advance();
		}

	}

	/**
	 * Sets computed rating on predicate for user.
	 * @param predicate is the name of predicate
	 * @param user is the name of user
	 */
	public void setIducedRatingOnPredicate(String predicate, String user) {
		if (predicate == null || predicate.equals(""))
			return;
		if (user == null || user.equals(""))
			return;
		int index = findIndexOfPredicate(predicate);
		if (index == connectingPredicates.length)
			return;

		// We initialize new instance of UserRatings
		UserRatings ur = new UserRatings(namespace, queryFinder, master,
				masterModifier);
		ur.setUserName(user);
		ur.setInducedRatingOnPredicate(predicate);
	}

	/**
	 * 
	 * @return name of atribute class, which was specified in constructor
	 */
	public String getAttributeClassName() {
		return attributeClassName;
	}

	/**
	 * Sets atribute class
	 * @param attributeClassName
	 */
	public void setAttributeClassName(String attributeClassName) {
		this.attributeClassName = attributeClassName;
	}

	/**
	 * 
	 * @return predicates, that connect attribute class to other classes
	 */
	public String[] getConnectingPredicates() {
		return connectingPredicates;
	}

	/**
	 * Sets predicates, that connect attribute class to other classes
	 * @param connectingPredicates
	 */
	public void setConnectingPredicates(String connectingPredicates[]) {
		this.connectingPredicates = connectingPredicates;
	}

}
